package com.ruoyi.system.service.impl;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.en.DSType;
import com.ruoyi.system.mapper.ColumnMngMapper;
import com.ruoyi.system.mapper.DatabaseMngMapper;
import com.ruoyi.system.service.IColumnMngService;
import com.ruoyi.system.service.IDatabaseMngService;
import com.ruoyi.system.service.ITableMngService;
import com.ruoyi.system.utils.ComparePropertyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 数据源管理Service业务层处理
 *
 * @author weicc
 * @date 2023-09-23
 */
@Service
@Slf4j
public class DatabaseMngServiceImpl implements IDatabaseMngService {
    @Resource
    private DatabaseMngMapper databaseMngMapper;

    @Resource
    private ITableMngService tableMngService;

    @Resource
    private IColumnMngService columnMngService;

    @Resource
    private ColumnMngMapper columnMngMapper;

    /**
     * 查询数据源管理
     *
     * @param id 数据源管理主键
     * @return 数据源管理
     */
    @Override
    public DatabaseMng selectDatabaseMngById(String id) {
        return databaseMngMapper.selectDatabaseMngById(id);
    }

    /**
     * 查询数据源管理列表
     *
     * @param databaseMng 数据源管理
     * @return 数据源管理
     */
    @Override
    public List<DatabaseMng> selectDatabaseMngList(DatabaseMng databaseMng) {
        return databaseMngMapper.selectDatabaseMngList(databaseMng);
    }

    /**
     * 新增数据源管理
     *
     * @param databaseMng 数据源管理
     * @return 结果
     */
    @Override
    public int insertDatabaseMng(DatabaseMng databaseMng) {
        databaseMng.setCreateTime(DateUtils.getNowDate());
        databaseMng.setId(UUID.fastUUID().toString());
        databaseMng.setDelFlag(Constants.DEL_FLAG_0);

        OperateDBDTO operateDBDTO = new OperateDBDTO();
        BeanUtils.copyBeanProp(operateDBDTO, databaseMng);

        databaseMng.setUrl(DSType.valueOf(databaseMng.getDbType()).getUrl(operateDBDTO));
        databaseMng.setDriver(DSType.valueOf(databaseMng.getDbType()).getDriverName());
        // 测试连接
        if (!verifyDataSource(databaseMng)) {
            throw new BaseException("当前数据源无法连接，请输入正确的配置信息");
        }
        return databaseMngMapper.insertDatabaseMng(databaseMng);
    }

    /**
     * 修改数据源管理
     *
     * @param databaseMng 数据源管理
     * @return 结果
     */
    @Override
    public int updateDatabaseMng(DatabaseMng databaseMng) {
        databaseMng.setUpdateTime(DateUtils.getNowDate());

        OperateDBDTO operateDBDTO = new OperateDBDTO();
        BeanUtils.copyBeanProp(operateDBDTO, databaseMng);

        databaseMng.setUrl(DSType.valueOf(databaseMng.getDbType()).getUrl(operateDBDTO));
        databaseMng.setDriver(DSType.valueOf(databaseMng.getDbType()).getDriverName());
        // 测试连接
        if (!verifyDataSource(databaseMng)) {
            throw new BaseException("当前数据源无法连接，请输入正确的配置信息");
        }
        return databaseMngMapper.updateDatabaseMng(databaseMng);
    }

    /**
     * 批量删除数据源管理
     *
     * @param ids 需要删除的数据源管理主键
     * @return 结果
     */
    @Override
    public int deleteDatabaseMngByIds(String[] ids) {
        return databaseMngMapper.deleteDatabaseMngByIds(ids);
    }

    /**
     * 删除数据源管理信息
     *
     * @param id 数据源管理主键
     * @return 结果
     */
    @Override
    public int deleteDatabaseMngById(String id) {
        return databaseMngMapper.deleteDatabaseMngById(id);
    }

    /**
     * verifyDataSource
     *
     * @param databaseMng
     * @return
     */
    @Override
    public Boolean verifyDataSource(DatabaseMng databaseMng) {
        OperateDBDTO operateDBDTO = new OperateDBDTO();
        BeanUtils.copyBeanProp(operateDBDTO, databaseMng);

        operateDBDTO.setUrl(DSType.valueOf(operateDBDTO.getDbType()).getUrl(operateDBDTO));
        operateDBDTO.setDriver(DSType.valueOf(operateDBDTO.getDbType()).getDriverName());
        Connection connection = DSType.getConnection(operateDBDTO);
        if (connection == null) {
            return false;
        }
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭数据库连接失败, 提示:[{}]", e.getMessage());
        }
        return true;
    }

    /**
     * 同步数据员
     *
     * @param dbId
     */
    @Override
    public void synchronizeDataSource(String dbId) {
        // 读出所有的库
        DatabaseMng database = databaseMngMapper.selectDatabaseMngById(dbId);
        OperateDBDTO operateDBDTO = new OperateDBDTO();
        BeanUtils.copyBeanProp(operateDBDTO, database);
        // 拉取最新的元数据
        Map<String, OperateStructureDTO> newTableMap;

        newTableMap = DSType.getStructureDTO(operateDBDTO);

        // 读出库所有的结构
        Map<String, OperateStructureDTO> oldTableMap = listStructureDTO(operateDBDTO.getId());

        // 1-表
        // 1-1级联新增表及列
        for (Map.Entry<String, OperateStructureDTO> tableEntry : newTableMap.entrySet()) {
            OperateStructureDTO oldTable = oldTableMap.get(tableEntry.getKey());
            if (oldTable == null) {
                OperateStructureDTO dto = tableEntry.getValue();
                dto.setDbId(operateDBDTO.getId());
                tableMngService.saveStructure(dto);
            }
        }

        // 1-2判断表更新(删除及修改)
        for (Map.Entry<String, OperateStructureDTO> tableEntry : oldTableMap.entrySet()) {
            OperateStructureDTO newTable = newTableMap.get(tableEntry.getKey());

            // 1-2-1删除表
            if (newTable == null) {
                tableMngService.deleteStructure(tableEntry.getValue());
            }
            // 1-2-2修改表
            else {
                List<ModifiedDTO> diffTables = ComparePropertyUtils.getDiff(newTable, tableEntry.getValue());
                if (!CollectionUtils.isEmpty(diffTables)) {
                    newTable.setId(tableEntry.getValue().getId());
                    tableMngService.updateStructure(newTable);
                }
                synchronizeColumn(tableEntry, newTable);
            }
        }

    }

    private void synchronizeColumn(Map.Entry<String, OperateStructureDTO> tableEntry, OperateStructureDTO newTable) {
        Map<String, SaveColumnDTO> oldColumnMap = tableEntry.getValue().getColumnMap();

        // 2-1新增列
        for (Map.Entry<String, SaveColumnDTO> columnEntry : newTable.getColumnMap().entrySet()) {
            SaveColumnDTO oldColumn = oldColumnMap.get(columnEntry.getKey());
            if (oldColumn == null) {
                SaveColumnDTO newColumn = columnEntry.getValue();
                newColumn.setTableId(tableEntry.getValue().getId());
                columnMngService.saveColumns(newColumn);
            }
        }

        // 2-2判断列更新(删除及修改)
        for (Map.Entry<String, SaveColumnDTO> columnEntry : oldColumnMap.entrySet()) {
            SaveColumnDTO newColumn = newTable.getColumnMap().get(columnEntry.getKey());

            // 2-2-1删除列
            if (newColumn == null) {
                Optional.ofNullable(columnMngMapper.selectColumnMngById(columnEntry.getValue().getId())).ifPresent(columnMngEntity -> {
                    columnMngEntity.setDelFlag("1");
                    columnMngMapper.updateById(columnMngEntity);
                });
            }
            // 2-2-2修改列
            else {
                List<ModifiedDTO> diffColumns = ComparePropertyUtils.getDiff(newColumn, columnEntry.getValue());
                if (!CollectionUtils.isEmpty(diffColumns)) {
                    newColumn.setId(columnEntry.getValue().getId());
                    columnMngService.updateColumn(newColumn);
                }
            }
        }
    }

    private Map<String, OperateStructureDTO> listStructureDTO(String id) {
        Map<String, OperateStructureDTO> tableMap = databaseMngMapper.listTableDTO(id);
        if (tableMap == null || tableMap.values().size() == 0) {
            return new HashMap<>();
        }
        for (OperateStructureDTO tableDTO : tableMap.values()) {
            tableDTO.setColumnMap(listColumnDTO(tableDTO.getId()));
        }
        return tableMap;
    }

    private Map<String, SaveColumnDTO> listColumnDTO(String id) {
        return Optional.ofNullable(databaseMngMapper.listColumnDTO(id)).orElse(new HashMap<>());
    }
}
